#ifndef __MYSQL_CONNECTION_H_
#define __MYSQL_CONNECTION_H_

#include "mysql_result.h"
#include <assert.h>
#include <functional>

class MysqlConnection final
{
public:
	MysqlConnection();
	~MysqlConnection();
	MysqlConnection(const MysqlConnection&) = delete;
	MysqlConnection& operator=(const MysqlConnection&) = delete;
	MysqlConnection(MysqlConnection&& obj);
	MysqlConnection& operator=(MysqlConnection&& obj);

public:
	bool Connect(const char* db, const char* user, const char* passwd, const char* host = "localhost", unsigned int port = 3306, const char* unix_socket = nullptr, unsigned long clientflag = 0);
	bool Exec(const char* sql);
	bool Exec(const char* sql, unsigned long len);
	MysqlResult Query(const char* sql);
	MysqlResult Query(const char* sql, unsigned long len);

	template
	<
		typename Fn,
		typename... Args,
		typename = typename std::enable_if
		<
		std::is_assignable
		<
		std::function<typename std::result_of<Fn(Args...)>::type(Args...)>,
		typename std::decay<Fn>::type
		>::value &&
		!std::is_same<typename std::result_of<Fn(Args...)>::type, void>::value
		>::type
	>
	inline auto Transaction(Fn&& func, Args&&... args) -> typename std::result_of<Fn(Args...)>::type
	{
		Query("begin;");
		try
		{
			auto res = func(std::forward<Args>(args)...);
			Query("commit;");
			return res;
		}
		catch (const char*)
		{
			Query("rollback;");
			return NULL;
		}
		catch (...)
		{
			Query("rollback;");
			return NULL;
		}
	}

	template
	<
		typename Fn,
		typename... Args,
		typename = typename std::enable_if
		<
		std::is_same<typename std::result_of<Fn(Args...)>::type, void>::value
		>::type
	>
	inline void Transaction(Fn&& func, Args&&... args)
	{
		Query("begin;");
		try
		{
			func(std::forward<Args>(args)...);
			Query("commit;");
		}
		catch (const char*)
		{
			Query("rollback;");
		}
		catch (...)
		{
			Query("rollback;");
		}
	}

private:
	MYSQL* _conn;
};

#endif